/**
 * 根据目前处理执行状态的函数调用次数对函数进行节流处理，当目前处于执行状态的函数调用数已达限制，则不再执行函数，
 * 直到有函数调用执行完成使得目前处于执行状态的函数调用数降低到限制以下时，才能够执行函数。
 *
 * @param {(...args: any[]) => Promise<any>} fun 要进行节流处理的函数，<b style="color:red">该函数必须返回 Promise</b>
 * @param {string} tag 目前处于执行状态的调用次数标记名称，默认为'_throttle'
 * @param {number} limit 同时处理执行状态的函数调用次数限制，默认为 1
 * @param {any} thisArg 指定需要节流处理的函数(传统函数)调用时的 this 指向，默认为 null，即非严格模式下函数调用时的 this 指向全
 * 局对象，严格模式下为 thisArg 指定的值；对于需要节流处理的函数为箭头函数时，该参数无效，箭头函数的 this 指向由函数定义时确定，不受
 * thisArg 参数影响，箭头函数的 this 来源于箭头函数定义时的上级作用域。
 * @returns {(...args: any[]) => Promise<any>} 节流处理后的函数
 */
export const funThrottleByLimit = (
  {
    fun,
    tag = '_throttle',
    limit = 1,
    thisArg = null,
  }: {
    fun: (...args: any[]) => Promise<any>,
    tag?: string;
    limit?: number;
    thisArg?: any;
  }
): (...args: any[]) => Promise<any> => {
  // 为函数添加目前处于执行状态的调用次数标记
  Reflect.defineProperty(fun, tag, {value: 0, writable: true})
  return async (...args: any[]): Promise<void | any> => {
    // 如果目前处于执行状态的函数调用已达限制
    if (Reflect.get(fun, tag) as number >= limit) return
    // 处于执行状态的函数调用次数加一
    Reflect.set(fun, tag, (Reflect.get(fun, tag) as number) + 1)
    // 调用函数
    const res = await fun.apply(thisArg, args)
    // 处于执行状态的函数调用次数减一
    Reflect.set(fun, tag, (Reflect.get(fun, tag) as number) - 1)
    // 返回函数调用结果
    return res
  }
}


/**
 * 根据调用函数的时间间隔对函数进行节流处理，当目前调用函数的时间距离上次函数调用时间的间隔小于指定间隔，则不执行函数的调用，直到函数调用
 * 的时间与上次函数调用时间的间隔大于或等于指定间隔时，才执行函数的调用。
 *
 * @param {(...args: any[]) => Promise<any>} fun 要进行节流处理的函数，<b style="color:red">该函数必须返回 Promise</b>
 * @param {number} interval 相邻两次函数调用的时间间隔，单位为 ms，默认为 500ms
 * @param {any} thisArg 指定需要节流处理的函数(传统函数)调用时的 this 指向，默认为 null，即非严格模式下函数调用时的 this 指向全
 * 局对象，严格模式下为 thisArg 指定的值；对于需要节流处理的函数为箭头函数时，该参数无效，箭头函数的 this 指向由函数定义时确定，不受
 * thisArg 参数影响，箭头函数的 this 来源于箭头函数定义时的上级作用域。
 * @returns {(...args: any[]) => Promise<any>} 节流处理后的函数
 */
export const funThrottleByTime = (
  {
    fun,
    interval = 500,
    thisArg = null,
  }: {
    fun: (...args: any[]) => Promise<any>,
    interval?: number;
    thisArg?: any;
  }
): (...args: any[]) => Promise<any> => {
  // 记录上次函数调用时间
  let lastTime = 0
  return async (...args: any[]): Promise<void | any> => {
    // 获取当前调用函数时的时间
    const now = Date.now()
    // 如果距离上次函数调用时间间隔小于指定间隔，则不执行函数
    if (now - lastTime < interval) return
    // 更新上次函数调用时间
    lastTime = now
    // 调用函数，并返回函数调用结果
    return await fun.apply(thisArg, args)
  }
}
